package com.hitqz.robot.biz.business.dispatch.impl;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import com.hitqz.robot.api.business.enums.RobotDriverEnum;
import com.hitqz.robot.api.business.enums.WorkStatusEnum;
import com.hitqz.robot.api.common.dto.info.RobotNavInfoDto;
import com.hitqz.robot.api.common.dto.robot.RobotGoTargetPosDto;
import com.hitqz.robot.api.common.dto.warning.AddWarningDto;
import com.hitqz.robot.api.common.entity.MapArea;
import com.hitqz.robot.api.common.entity.Robot;
import com.hitqz.robot.api.common.entity.RobotMapPos;
import com.hitqz.robot.api.dispatch.protocol.dto.GotoTargetDto;
import com.hitqz.robot.api.dispatch.protocol.dto.RobotMapPosDto;
import com.hitqz.robot.api.dispatch.protocol.dto.RobotNextStepDto;
import com.hitqz.robot.biz.business.dispatch.DispatchService;
import com.hitqz.robot.biz.config.RobotProperties;
import com.hitqz.robot.biz.schedule.CheckNavStatusSchedule;
import com.hitqz.robot.biz.service.RobotMapPosService;
import com.hitqz.robot.biz.service.RobotService;
import com.hitqz.robot.driver.model.IRobotPosDto;
import com.hitqz.robot.driver.parent.RobotDriverService;
import com.hitqz.robot.netty.service.Send2ServerService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.operation.distance.DistanceOp;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author xupkun
 * @date 2024/6/6
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class DispatchServiceImpl implements DispatchService {
    private final RobotService robotService;

    private final RobotDriverService robotDriverService;

    private final RestTemplate restTemplate;

    private final String url = "http://127.0.0.1:9999/admin/agv/info";

    private final RobotProperties robotProperties;

    private final Send2ServerService send2ServerService;

    Cache<String, GotoTargetDto> cache = CacheBuilder.newBuilder()
            .expireAfterWrite(1, TimeUnit.MINUTES)
            .maximumSize(100)
            .build();

    @Override
    public boolean go2TargetPosition(RobotMapPos mapPos, boolean charge) {
        return go2TargetPosition(mapPos, charge, true);
    }

    /**
     * @param mapPos
     * @param charge
     * @param needDispatch 是否需要调度
     * @return
     */
    @Override
    public boolean go2TargetPosition(RobotMapPos mapPos, boolean charge, boolean needDispatch) {
        Robot robot = robotService.getDefaultRobot();

        try {
            if (needDispatch) {
                Cache<String, RobotMapPos> navTarget = SpringUtil.getBean("navTarget");
                navTarget.put(robot.getRobotSn(), mapPos);
                IRobotPosDto iRobotPosDto = robotDriverService.getWebPos();
                send2ServerService.applyGotoTarget(robot, iRobotPosDto.getLastStation(), mapPos.getCode());
            } else {
                String taskId = UUID.randomUUID().toString().replace("-", "");
                robotDriverService.robotTaskGoTarget(
                        RobotGoTargetPosDto.builder()
                                .robot(robot)
                                .code(mapPos.getCode())
                                .charge(charge)
                                .posId(mapPos.getId().intValue())
                                .taskId(taskId)
                                .path(Lists.newArrayList())
                                .build()
                );
                this.retryGotoTarget(taskId, mapPos, charge);
            }
            return true;
        } catch (IllegalStateException e) {
            AddWarningDto addWarningDto = new AddWarningDto();
            addWarningDto.setCode("0");
            addWarningDto.setTitle(robot.getRobotModel() + "报警");
            addWarningDto.setContent(e.getMessage());
            addWarningDto.setSuggestion("无");
            addWarningDto.setLevel(1);
            addWarningDto.setStopTask(true);
            SpringUtil.getApplicationContext().publishEvent(addWarningDto);
            log.warn("go station IllegalStateException:" + mapPos.getCode());
        } catch (Exception e) {
            // 记录更详细的错误信息
            log.error("An error occurred while attempting to move the robot to target position: {}", mapPos, e);
        }
        return false;
    }

    private static ExecutorService executorService = Executors.newSingleThreadExecutor();
    private static FutureTask<String> futureTask = null;
    private final ReentrantLock retryGotoTargetLock = new ReentrantLock();

    /**
     * 补发前往目标点位
     *
     * @param taskId
     * @param mapPos
     * @param charge
     */
    private void retryGotoTarget(String taskId, RobotMapPos mapPos, boolean charge) {
        try {
            retryGotoTargetLock.lock();
            IRobotPosDto webPos = robotDriverService.getWebPos();
            String sourcePos = webPos.getLastStation();
            String targetPos = mapPos.getCode();
            Integer targetPosId = mapPos.getId().intValue();
            if (StringUtils.hasText(sourcePos) && !sourcePos.equals(targetPos)) {
                cache.put(taskId, GotoTargetDto.builder().sourcePos(sourcePos)
                        .targetPos(targetPos).targetPosId(targetPosId)
                        .charge(charge).build());
                if (futureTask != null && !futureTask.isDone()) {
                    boolean b = futureTask.cancel(true);
                    if (b) {
                        log.info("取消上一个补发任务成功！");
                    }
                }
                //补发逻辑，防止发送成功但是机器人就定住没走
                futureTask = new FutureTask<String>(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(5);
                        GotoTargetDto dto = cache.getIfPresent(taskId);
                        IRobotPosDto iRobotPosDto = robotDriverService.getWebPos();
                        Robot currentRobot = robotService.getDefaultRobot();
                        AtomicInteger cnt = new AtomicInteger(0);
                        // 导航状态为已完成且当前位置为源点 且补发次数满足
//                    log.info("判断==》机器人导航状态：{} 执行前点位:{},当前点位：{},目标点位{}", iRobotPosDto.getTaskStatus(),
//                            dto.getSourcePos(),
//                            iRobotPosDto.getLastStation(),
//                            dto.getTargetPos()
//                    );
                        while (currentRobot.getWorkStatus().equals(WorkStatusEnum.WORK.getCode())
                                && iRobotPosDto.getTaskStatus() != null && 4 == iRobotPosDto.getTaskStatus().intValue()
                                && dto.getSourcePos().equals(iRobotPosDto.getLastStation())
                                && cnt.getAndIncrement() < 3) {//尝试3次
                            log.info("机器人导航状态：{} 当前点位：{},目标点位{}，开始第{}次补发前往目标点位...",
                                    iRobotPosDto.getTaskStatus(),
                                    iRobotPosDto.getLastStation(),
                                    dto.getTargetPos(),
                                    cnt.get()
                            );
                            currentRobot = robotService.getDefaultRobot();
                            robotDriverService.robotTaskGoTarget(
                                    RobotGoTargetPosDto.builder()
                                            .robot(currentRobot)
                                            .code(dto.getTargetPos())
                                            .charge(dto.isCharge())
                                            .posId(dto.getTargetPosId().intValue())
                                            .taskId(UUID.randomUUID().toString().replace("-", ""))
                                            .path(Lists.newArrayList())
                                            .build()
                            );
                            iRobotPosDto = robotDriverService.getWebPos();
                            TimeUnit.SECONDS.sleep(5);
                        }
                    } catch (Exception e) {
                        log.error("前往点位补发失败：{}", e.getMessage());
                    }
                    return "";
                });
                executorService.submit(futureTask);
            }
        } finally {
            retryGotoTargetLock.unlock();
        }
    }

}
